#include "command.h"
#include "common.h"
#include <string.h>
#include <iostream>
#include <sys/socket.h>
#include <memory>
#include <unistd.h>
#include <string>
#include "md5.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace std;

// 声明一下全部变量
extern string dirPath;
void cmd_ls_handler(int sockfd, string &input)
{
    // 组装数据包
    CommandData data;
    strcpy(data.cmd, "ls");
    strcpy(data.args, dirPath.c_str());
    int ret = SendPacket(sockfd, TYPE_DIR, &data, sizeof(CommandData));
    CheckErr(ret, "SendPacket err");
    
    // recv
    char buf[1024] = {0};
    ret = RecvPacket(sockfd, buf, 1024);
    CheckErr(ret, "RecvPacket err");

    DataPacket *rpd = (DataPacket*)buf;
    cout << rpd->data << endl;
}

void cmd_rm_handler(int sockfd, string &input)
{
    int ret = 0;
    char buf[1024] = {0};
    // 从input接收要删除的文件名   rm file1
    int idx = input.find("rm");
    if (idx+2 == input.size())  // rm
    {
        cout << "命令输入不合法" << endl;
        return;
    }
    string filename = input.substr(idx+2, input.size()-idx-2);
    // 去掉filename的前后的空格
    filename = Trim(filename);
    if (filename.size() == 0)
    {
        cout << "命令输入不合法" << endl;
        return;
    }

    // 打包数据发送到server
    CommandData cmd_data;
    strcpy(cmd_data.cmd, "rm");
    string filepath = dirPath + filename;
    strcpy(cmd_data.args, filename.c_str());
    ret = SendPacket(sockfd, TYPE_DIR, &cmd_data, sizeof(CommandData));
    CheckErr(ret, "SendPacket err");

    // 接收server端的响应开始校验
    DataPacket *dp = NULL;
    
    ret = RecvPacket(sockfd, buf, 1024);
    CheckErr(ret, "RecvPacket err");
    dp = (DataPacket*)buf;
    if (dp->type == TYPE_ACK)
    {
        cout << "请检查文件名是否合法" << endl;
        return;
    }
    else if (dp->type == TYPE_AUTH)
    {
        // 输入密码发送到server端
        cout << "输入密码:";
        string pwd;
        cin >> pwd;
        getchar();    // 解决结束后路径重复出现的问题
        ret = SendPacket(sockfd, TYPE_ACK, (void*)(pwd.c_str()), pwd.size());
        CheckErr(ret, "SendPacket err");

        // 接收server端的响应
        ret = RecvPacket(sockfd, buf, 1024);
        CheckErr(ret, "RecvPacket err");
        dp = (DataPacket*)buf;
        if (strncmp(dp->data, "ok", 2) == 0)
        {
            cout << "删除文件:" << filename << " 成功!" << endl;
        }
        else
        {
            cout << "密码不正确!" << endl;
        }
    }
    else
    {
        cout << "不能识别的消息:" << dp->type << endl;
        return;
    }
}

void cmd_cd_handler(int sockfd, string &input)
{
    int ret = 0;
    char buf[1024] = {0};
    // 从input接收要打开的文件名 
    int idx = input.find("cd");
    if (idx+2 == input.size())  
    {
        cout << "命令输入不合法" << endl;
        return;
    }
    string filename = input.substr(idx+2, input.size()-idx-2);
    // 去掉filename的前后的空格
    filename = Trim(filename);
    if (filename.size() == 0)
    {
        cout << "命令输入不合法" << endl;
        return;
    }
    //如果参数是..,需要返回上一级目录
    if (filename == ".." || filename == "../")
    {
        //如果此时已经在根目录，则提示用户
        if(dirPath == "./")
        {
            cout<<"当前已在根目录!"<<endl;
    
        }
        //如果此时不在根目录，则把最后一级目录去掉
        else
        {
            //如果最后一个字符是/的话，先把最后一个/去掉
            if( dirPath[dirPath.size()-1] == '/')
            {
                dirPath = dirPath.substr(0,dirPath.size()-1);
            }
            //然后找到最新字符串的最后一个/,把/前面的作为总路径
            int num = dirPath.find_last_of("/");
            dirPath = dirPath.substr(0,num+1);
        }
    }
    //如果参数是.，保持当前目录
    else if (filename == "." || filename == "./")
    {
        return;
    }
    //如果都不是,把总路径发送给服务端，让服务端判断文件是否存在，存在回复ok
    else
    {
        // 打包数据发送到server
        CommandData cmd_data;
        strcpy(cmd_data.cmd, "cd");

        string filepath = dirPath +  filename;
        strcpy(cmd_data.args, filepath.c_str());
        ret = SendPacket(sockfd, TYPE_DIR, &cmd_data, sizeof(CommandData));
        CheckErr(ret, "SendPacket err");

        // 接收server端的响应
        DataPacket *dp = NULL;
        ret = RecvPacket(sockfd, buf, 1024);
        CheckErr(ret, "RecvPacket err");
        dp = (DataPacket*)buf;
        //cout<<dp->data<<endl;
        //判断，成功则打印，失败则提示文件不存在。
        string res = "ok";
        if(strcmp(dp->data,res.c_str()) == 0)
        {
            //拼接新路径，更新到dirpath
            //如果用户输入的目录名最后没加/,则在这里添加
            if(filename[filename.size()-1] != '/')
            {
                dirPath = dirPath +  filename + "/";
            }
            else
            {
                dirPath = dirPath +  filename;
            }
        }
        else
        {
            cout<<"文件夹不存在!"<<endl;
        }
    }
}

void cmd_download_handler(int sockfd, string &input)
{

}

void cmd_upload_handler(int sockfd, string &input)
{
    // upload  本地的一个文件   默认就上传到当前目录中
    // 用户输入的文件名是否合法  access检查
    // upload  ~/test.cc
    int ret = 0;
    input = Trim(input);
    if (input == "upload")
    {
        cout << "命令输入不合法" << endl;
        return;
    }
    string filepath = input.substr(strlen("upload"), input.size()-strlen("upload"));
    // 去掉filepath的前后的空格
    filepath = Trim(filepath);
    if (filepath.size() == 0 || access(filepath.c_str(), F_OK) != 0 )
    {
        cout << "输入的文件名不合法！" << endl;
        return;
    }

    // 从filepath中把filename截取出来
    int pos = filepath.find_last_of('/');
	if (pos == string::npos)
	{
		cout << "输入的文件名的绝对路径！" << endl;
        return;
	}
    string filename = filepath.substr(pos+1, filepath.size() - pos -1);

    // 读取文件的文件名（dirPath+'/'+filename）和大小，计算文件的md5值
    // 打包发送到server端   filepath、filesize、totalblock、md5
    string server_filepath = dirPath + filename;

    // 获取文件的大小
    FILE* fp = fopen(filepath.c_str(), "r");
    CheckErr(fp, "fopen err");
    fseek(fp, 0, SEEK_END);
    uint64 fileSize = ftell(fp);
    fclose(fp);
	
    // 计算totalblock、md5
    int totalblock = fileSize / BLOCK_SIZE;
    if (fileSize % BLOCK_SIZE != 0)
        totalblock++;
    char* md5 = MD5_file(filepath.c_str());
    auto_ptr<char> ptr(md5);

    // 打包文件，上传给服务器
    UploadFile data;
    strcpy(data.filepath, server_filepath.c_str());
    data.filesize = fileSize;
    strcpy(data.md5, md5);     // ----------------core文件显示：程序挂掉这里了
    ret = SendPacket(sockfd, TYPE_UPLOAD, (void*)&data, sizeof(data));
    CheckErr(ret, "SendPacket err");

    // 接收server端的响应 
    // 如果file已经存在,判断从服务端接收到的响应类型
    // 如果file不存在，开始传输
    // state、blockno
    char buf[1024] = {0};
    ret = RecvPacket(sockfd, buf, 1024);
    CheckErr(ret, "RecvPacket err");

    // 接收到的类型进行判断
    DataPacket* dp = (DataPacket*)buf;
    UploadFileAck uploadFile;
    memcpy(&uploadFile, dp->data, dp->datalen);

    if(uploadFile.type == TYPE_QUICK)
    {
        cout <<"我是秒传：文件已发送成功"<< endl;
        return;
    }
    
	if(uploadFile.type == TYPE_NORMAL)
    {
        // 这里传输文件，应该是一个循环的过程
        // blockno、content
        // 开始按块发送文件数据，server端每收到完整的一块数据，写文件，响应ACK(进度...%10)
        int num = uploadFile.blockno;
        //FILE *fp = fopen(filepath.c_str(), "r");
        //CheckErr(fp, "RecvPacket err");
        int fd = open(filepath.c_str(), O_RDONLY);
        CheckErr(fd, "open err");
        //fseek(fp, (num-1)*BLOCK_SIZE, SEEK_SET); // 从返回的块数开始读取
        lseek(fd, (num-1)*BLOCK_SIZE, SEEK_SET);

        while(num < totalblock)
        {
            //定义一个块,并打包，发送块文件
            TransferFile blockfile;
            blockfile.blockno = num;
            
            // 读取文件，每次读的大小为一个数据块
            char buf[BLOCK_SIZE] = {0};
            //int readsize = fread(buf, 1, BLOCK_SIZE, fp);
            int readsize = read(fd, buf, BLOCK_SIZE);

            //strncpy(blockfile.content, buf, readsize);
            memcpy(blockfile.content, buf, readsize);
            int size = sizeof(TransferFile);
            if (readsize < BLOCK_SIZE)
            {
                size = sizeof(int) + readsize;
            }
            ret = SendPacket(sockfd, TYPE_UPLOAD, (void*)&blockfile, size);
            CheckErr(ret, "SendPacket err");

            //接收回复
            char buff[1024] = {0};
            ret = RecvPacket(sockfd, buff, 1024);
            CheckErr(ret, "RecvPacket err");
            DataPacket* dp = (DataPacket*)buff;
            if(dp->type == TYPE_ACK)
            {
                // 显示进度
                if (strlen(dp->data) > 0)
                    cout <<".." << dp->data << " ";
            }
            num++;
        }
        cout << endl;
        close(fd);

        //跳出循环，文件传输结束，接收md5校验结果
        char buf[1024] = {0};
        ret = RecvPacket(sockfd, buf, 1024);
        CheckErr(ret, "RecvPacket err");
        DataPacket* dp = (DataPacket*)buf;

        cout << "dp->data:" << dp->data << endl;
        if(strncmp(dp->data, "ok", 2) == 0)
        {
            cout<<"文件传输完毕！"<<endl;
        }
        else
        {
            cout<<"文件传输失败！"<<endl;
        }
    }
}